package com.hq.ims.logincenter.service;

import com.hq.authority.token.ITokenGP;
import com.hq.ims.data.alisms.AliSmsConfig;
import com.hq.ims.data.config.verification.VdAdd;
import com.hq.ims.data.entity.*;
import com.hq.ims.data.entityenum.EnumAccountStatus;
import com.hq.ims.data.event.CompanyEvent;
import com.hq.ims.data.event.LogUserLoginInfoEvent;
import com.hq.ims.data.service.*;
import com.hq.ims.data.utils.AccountUtils;
import com.hq.ims.data.utils.ErrMsgConstant;
import com.hq.ims.data.utils.LocalConstant;
import com.hq.ims.data.utils.PasswordUtils;
import com.hq.ims.data.vo.UserMapstruct;
import com.hq.utils.exception.CommonException;
import com.hq.utils.util.*;
import com.hq.web.conf.bean.UserAttribute;
import com.hq.web.inf.token.TokenInfo;
import com.hq.web.inf.user.ITokenUserInfo;
import com.hq.web.penum.EnumDeviceType;
import com.hq.web.utils.SpringBootValidateUtils;
import com.hq.web.utils.WebUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * 登录中心的Service
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class LoginCenterBusService {

    @Resource
    private BaseUserService baseUserService;
    @Resource
    private RedisTemplate<String, ITokenUserInfo> tokenRedisTemplate;
    @Resource
    private BaseCompanyEmployeeService baseCompanyEmployeeService;
    @Resource
    private BaseCompanyService baseCompanyService;
    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private AliSmsConfig aliSmsConfig;
    @Value("${userAttribute.userDefaultAvatar}")
    private String initUserAvatar;
    @Resource
    private ITokenGP tokenGP;
    @Resource
    private UserAttribute userAttribute;
    @Resource
    private BaseCompanyDepartmentService baseCompanyDepartmentService;
    @Resource
    private BaseCompanyDepartmentEmployeeService baseCompanyDepartmentEmployeeService;


    /**
     * 账号密码登陆
     */
    public UserMapstruct.SuccessLoginVo accountPwdLogin(
            String enStr,
            String pushId
    ) {
        //获取前端传递的加密过后的账号密码字符串
        StringUtils.verificationEmpty(enStr, ErrMsgConstant.PARAM_INPUT_ERROR);

        //解密
        String deLoginInfo = EncryptDecryptUtils.rsaPrivateDecryption(enStr, LocalConstant.RSA_DECRY_KEY);
        StringUtils.verificationEmpty(deLoginInfo, ErrMsgConstant.DECRYPTION_ERR);

        int diffIndex = deLoginInfo.indexOf("-");
        String subAccount = deLoginInfo.substring(0, diffIndex);
        String suPwd = deLoginInfo.substring(diffIndex + 1);

        String[] accountPwdArr = new String[]{subAccount, suPwd};
        int sourceStrLength = 2;
        BooleanUtils.isTrueThrow(accountPwdArr.length != sourceStrLength, ErrMsgConstant.ACCOUNT_OR_PASSWORD_ERR);

        //明文账号
        String account = accountPwdArr[0];
        //明文密码
        String pwd = accountPwdArr[1];
        //客户端ID 一般在手机上用

        StringUtils.verificationEmpty(account, ErrMsgConstant.ACCOUNT_OR_PASSWORD_ERR);
        StringUtils.verificationEmpty(pwd, ErrMsgConstant.ACCOUNT_OR_PASSWORD_ERR);

        //加密后的用户密码
        String md5Pwd = EncryptDecryptUtils.md5Encrypt(pwd);
        return this.realLogin(account, md5Pwd, pushId);
    }

    /**
     * 手机号登陆
     */
    public UserMapstruct.SuccessLoginVo smsCodeLogin(
            String enStr,
            String pushId
    ) {
        //获取前端传递的加密过后的账号密码字符串
        StringUtils.verificationEmpty(enStr, ErrMsgConstant.PARAM_INPUT_ERROR);

        //解密
        String deLoginInfo = EncryptDecryptUtils.rsaPrivateDecryption(enStr, LocalConstant.RSA_DECRY_KEY);
        StringUtils.verificationEmpty(deLoginInfo, ErrMsgConstant.MOBILE_LOGIN_ERR);

        String[] accountSmsCodeArr = deLoginInfo.split("-");
        int sourceStrLength = 2;
        BooleanUtils.isTrueThrow(accountSmsCodeArr.length != sourceStrLength, ErrMsgConstant.MOBILE_LOGIN_ERR);

        //手机号
        String mobile = accountSmsCodeArr[0];
        //明文密码
        String smsCode = accountSmsCodeArr[1];
        //客户端ID 一般在手机上用

        StringUtils.verificationEmpty(mobile, ErrMsgConstant.MOBILE_VALID_ERR);
        BooleanUtils.isFalseThrow(RegexUtils.checkMobile(mobile), ErrMsgConstant.MOBILE_VALID_ERR);
        StringUtils.verificationEmpty(smsCode, ErrMsgConstant.MOBILE_SMS_CODE_ERR);
        //验证验证码
        Boolean smsCodeValidBl = aliSmsConfig.checkVerifyMobileSms(mobile, smsCode);
        BooleanUtils.isFalseThrow(smsCodeValidBl, ErrMsgConstant.MOBILE_SMS_CODE_ERR);

        String account = null;
        String md5Pwd = null;
        //加密后的用户密码,只允许注册过的用户登陆
        User user = baseUserService.getByMobile(mobile);
        ObjectUtils.isNullThrow(user, ErrMsgConstant.USER_INFO_NOT_EXISTS);

        account = user.getAccount();
        md5Pwd = user.getPwd();
        UserMapstruct.SuccessLoginVo vo = this.realLogin(account, md5Pwd, pushId);
        //最后删除验证码
        aliSmsConfig.deleteVerifyMobileSms(mobile);
        return vo;
    }

    /**
     * 真正实现登陆
     */
    public UserMapstruct.SuccessLoginVo realLogin(
            String account,
            String md5Pwd,
            String pushId
    ) {
        StringUtils.verificationEmpty(account, ErrMsgConstant.MOBILE_VALID_ERR);
        StringUtils.verificationEmpty(md5Pwd, ErrMsgConstant.PASSWORD_ERR);

        //根据账号获取用户信息
        User user = baseUserService.getByAccount(account);
        ObjectUtils.isNullThrow(user, ErrMsgConstant.MOBILE_UN_REGISTER_ERR);
        BooleanUtils.isTrueThrow(user.getIsNeedForceUpPwd(), "当前密码等级较弱,请先修改密码");

        //判该用户账号是否正常
        BooleanUtils.isTrueThrow(user.getAccountStatus() == EnumAccountStatus.Freeze, ErrMsgConstant.ACCOUNT_PROHIBIT_LOGIN);

        //数据库的密码
        String dataBasePwd = user.getPwd();

        //密码不相同
        if (!md5Pwd.equals(dataBasePwd)) {
            //屏蔽登录次数限制
            AccountUtils.limitTheNumberOfLoginErrors(account);
            throw CommonException.build(ErrMsgConstant.MOBILE_VALID_ERR);
        }
        //清空账号登录错误的次数
        AccountUtils.deleteAccountErrorLoginCount(account);

        //更新用户pushId
        if (StringUtils.isNotEmpty(pushId)) {
            user.setPushId(pushId);
            user.updateById();
        }

        //用户是否平台用户
        boolean isAllowLoginPlatform = baseUserService.isAllowLoginPlatform(user.getId());
        //字段临时用一下
        user.setIsAdmin(isAllowLoginPlatform);

        return loginGenerateToken(user);
    }

    /**
     * 登录生成token
     *
     * @param user
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public UserMapstruct.SuccessLoginVo loginGenerateToken(@NonNull User user) {
        //分pc和app
        EnumDeviceType clientType = WebUtils.getDevice();

        //清除token

        //生成token
        TokenInfo tokenInfo = new TokenInfo(user);
        tokenInfo.setUserId(user.getUserId());
        tokenInfo.setAccount(user.getAccount());

        //补充ITokenInfo信息
        tokenInfo.setFullUserInfo(user);
        //组装返回对象
        //使用指定token生成token
        String token = tokenGP.generateToken(tokenInfo);
        Long expired = userAttribute.getSafetyConfig().getTokenConfig().getTokenExpired();
        //声明返回对象
        UserMapstruct.SuccessLoginVo successLoginVo = new UserMapstruct.SuccessLoginVo();
        successLoginVo.setUserId(user.getUserId());
        successLoginVo.setToken(token);
        successLoginVo.setTokenExpire(expired);
        //将信息存入redis
//        tokenRedisTemplate.opsForValue().set(token, user, expired, TimeUnit.MILLISECONDS);

        //存储用户登录日志信息
        LogUserLoginInfo logUserLoginInfo = new LogUserLoginInfo();
        logUserLoginInfo.setLoginEquipment(WebUtils.getDevice());
        logUserLoginInfo.setLoginIp(WebUtils.getIp());
        logUserLoginInfo.setUserId(user.getUserId());
        logUserLoginInfo.setUserName(user.getRealName());
        logUserLoginInfo.setUserAccount(user.getAccount());
        applicationContext.publishEvent(new LogUserLoginInfoEvent(this, logUserLoginInfo));
        return successLoginVo;
    }


    /**
     * 注册
     */
    @Transactional(rollbackFor = Exception.class)
    public User mobileRegister(
            @NonNull UserMapstruct.MobileRegisterVo mobileRegisterVo
    ) {
        //数据校验
        SpringBootValidateUtils.validate(mobileRegisterVo);

        String mobile = mobileRegisterVo.getMobile();
        String code = mobileRegisterVo.getSmsCode();
        String pwd = mobileRegisterVo.getPwd();
        String realName = mobileRegisterVo.getRealName();

        //验证手机号格式
        BooleanUtils.isFalseThrow(RegexUtils.checkMobile(mobile), ErrMsgConstant.MOBILE_VALID_ERR);
        //1.校验手机号是否注册
        if (baseUserService.mobileExists(mobile, null)) {
            throw CommonException.build(ErrMsgConstant.MOBILE_ALREADY_EXISTS);
        }
        //2.校验验证码是否正确
        if (!aliSmsConfig.checkVerifyMobileSms(mobile, code)) {
            throw CommonException.build(ErrMsgConstant.MOBILE_SMS_CODE_ERR);
        }
        String decryptPwd = EncryptDecryptUtils.rsaPrivateDecryption(pwd, LocalConstant.RSA_DECRY_KEY);
        if (StringUtils.isEmpty(decryptPwd)) {
            throw CommonException.build(ErrMsgConstant.PASSWORD_ERR);
        }
        boolean pwdReg = PasswordUtils.validRegPwdRB(mobileRegisterVo.getPwd());
        BooleanUtils.isFalseThrow(pwdReg, ErrMsgConstant.PASSWORD_INTENSITY_LOW);
        //创建用户
        return baseUserService.addSimpleUserInfo(mobile, mobile, decryptPwd, initUserAvatar, realName);
    }

    @Transactional(rollbackFor = Exception.class)
    public void userAndCompanyRegister(
            @NonNull UserMapstruct.UserCompanyRegisterSaveVo vo
    ) {
        SpringBootValidateUtils.validate(vo, VdAdd.class);
        //注册用户
        User user = this.mobileRegister(vo);
        String companyId = StringUtils.EMPTY;
        Company company = null;
        if (vo.getIsRegisterNewCompany()) {
            SpringBootValidateUtils.validate(vo, UserMapstruct.VdRegisterNewCompany.class);
            //注册新公司
            company = baseCompanyService.addUserCompany(user, vo.getUserRegisterNewCompanySaveVo());
            companyId = company.getCompanyId();
            //创建默认部门
        } else {
            SpringBootValidateUtils.validate(vo, UserMapstruct.VdJoinCompany.class);
            companyId = vo.getJoinCompanyId();
            company = baseCompanyService.getById(companyId);
        }

        CompanyDepartment defaultDepartment = baseCompanyDepartmentService.getByCompanyIdAndCompanyDepartmentName(companyId, LocalConstant.DEFAULT_DEPARTMENT_NAME, null);
        if (defaultDepartment == null) {
            defaultDepartment = baseCompanyDepartmentService.createDefaultDepartment(companyId);
        }

        //加入公司下的默认部门
        baseCompanyDepartmentEmployeeService.joinCompany(companyId, user.getUserId(), defaultDepartment.getCompanyDepartmentId(), StringUtils.EMPTY, false);

        //如果是新建公司将用户设为管理员
        if (vo.getIsRegisterNewCompany()) {
            //设置用户为公司管理员
            baseCompanyEmployeeService.lambdaUpdate()
                    .eq(CompanyEmployee::getUserId, user.getUserId())
                    .eq(CompanyEmployee::getCompanyId, companyId)
                    .set(CompanyEmployee::getIsAdmin, true)
                    .update();
        }

        aliSmsConfig.deleteVerifyMobileSms(vo.getMobile());

        //发送事件
        if (vo.getIsRegisterNewCompany()) {
            applicationContext.publishEvent(new CompanyEvent(this, company, CompanyEvent.REGISTER_COMPANY));
        } else {
            applicationContext.publishEvent(new CompanyEvent(this, company, CompanyEvent.USER_JOIN_COMPANY));
        }
    }

    /**
     * 退出登陆
     */
    public void loginOut(
            String token
    ) {
        tokenRedisTemplate.delete(token);
    }

    /**
     * 找回密码
     */
    @Transactional(rollbackFor = Exception.class)
    public void forgotPassword(UserMapstruct.ForgotPasswordVo forgotPasswordVo) {
        //数据校验
        SpringBootValidateUtils.validate(forgotPasswordVo);

        //1: 验证手机号
        BooleanUtils.isFalseThrow(RegexUtils.checkMobile(forgotPasswordVo.getMobile()), ErrMsgConstant.MOBILE_VALID_ERR);
        User user = baseUserService.getByMobile(forgotPasswordVo.getMobile());
        ObjectUtils.isNullThrow(user, ErrMsgConstant.MOBILE_UN_REGISTER_ERR);

        //2：将密码解密验证
        String decryptPwd = EncryptDecryptUtils.rsaPrivateDecryption(forgotPasswordVo.getPwd(), LocalConstant.RSA_DECRY_KEY);
        if (StringUtils.isEmpty(decryptPwd)) {
            throw CommonException.build(ErrMsgConstant.DECODE_FAILURE);
        }

        boolean pwdReg = PasswordUtils.validRegPwdRB(forgotPasswordVo.getPwd());
        BooleanUtils.isFalseThrow(pwdReg, ErrMsgConstant.PASSWORD_INTENSITY_LOW);

        //4: 更新密码
        baseUserService.updateUserPwd(user.getUserId(), decryptPwd);
    }
}
